﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using TimeTracker.Provider;
using TimeTracker.SkinProvider;
using Utility;
using Utility.Windows.Forms;

namespace TimeTracker
{
    public partial class TreeViewLog : System.Windows.Forms.Form
    {
        public static TreeViewLog Instance
        {
            get
            {
                return Utility.Patterns.Singleton<TreeViewLog>.Instance;
            }
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            Program.MinimizeMemory();
        }

        public TreeViewLog()
        {
            InitializeComponent();

            this.treeWorker.WorkerSupportsCancellation = true;
            
            //initialize skin tree
            SkinManager.Instance.InitializeTreeView(this.treeView1);

            this.comboBox1.SelectedItem = Properties.Settings.Default.OrderBy;
            this.comboBox2.SelectedItem = Properties.Settings.Default.GroupBy;

            this.Update();
            this.Bind();
        }


        BackgroundWorker treeWorker = new BackgroundWorker();

        protected void Bind()
        {
            if (this.treeWorker.IsBusy) { return; }

            this.treeView1.SafeInvoke(delegate
            {
                this.treeView1.Nodes.Clear();
            });

            TreeNode rootNode = new TreeNode();
            rootNode.Expand();

            if (this.IsGroupByProject)
            {
                IEnumerable<IProject> projects = Manager.Instance.Projects;
                this.treeView1.SafeInvoke(delegate
                {
                    rootNode.Tag = projects;
                });
            }
            else if (!this.IsGroupByProject)
            {
                IEnumerable<DateTime> days = Manager.Instance.GetDaysBySession();
                this.treeView1.SafeInvoke(delegate
                {
                    rootNode.Tag = days;
                });
            }

            this.treeView1.SafeInvoke(delegate
            {
                this.treeView1.Nodes.Add(rootNode);
            });

            treeWorker.DoWork += new DoWorkEventHandler(delegate(object sender2, DoWorkEventArgs e2)
            {
                this.BindNodes(rootNode, true, e2);
                this.treeView1.SafeInvoke(delegate
                {
                    this.Sort();
                });
                this.SkinTree();
            });
            treeWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate(object sender2, RunWorkerCompletedEventArgs e2)
            {
                if (e2.Cancelled) { return; }

                if (this.treeView1.Nodes.Count == 1)
                {
                    this.treeView1.SafeInvoke(delegate
                    {
                        this.treeView1.Nodes[0].Expand();
                    });
                }
            });

            if (!this.treeWorker.IsBusy)
            {
                treeWorker.RunWorkerAsync();
            }
        }

        protected bool NodeMouseDown
        {
            get;
            set;
        }

        private void treeView1_NodeMouseDown(object sender, TreeNodeMouseClickEventArgs e)
        {
            this.treeView1.SelectedNode = e.Node;

            this.NodeMouseDown = true;

            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                this.edit.Visible = false;
                this.remove.Visible = false;
                this.addNote.Visible = false;
                this.addSession.Visible = false;
                this.addProject.Visible = false;
                this.archiveSessionsToolStripMenuItem.Visible = false;

                if (e.Node.Tag is IEnumerable<IProject>)
                {
                    this.addProject.Visible = Manager.Instance.CanCreateProject();
                }
                else if (e.Node.Tag is DateTime)
                {
                    this.archiveSessionsToolStripMenuItem.Visible = true;
                }
                else if (e.Node.Tag is IProject)
                {
                    IProject project = e.Node.Tag as IProject;

                    this.edit.Visible = Manager.Instance.CanEditProject(project);
                    this.remove.Visible = Manager.Instance.CanDeleteProject(project);
                    this.addSession.Visible = Manager.Instance.CanCreateSession(project);
                    this.addProject.Visible = Manager.Instance.CanCreateProject(project);
                }
                else if (e.Node.Tag is ISession)
                {
                    ISession session = e.Node.Tag as ISession;

                    this.edit.Visible = Manager.Instance.CanEditSession(session);
                    this.remove.Visible = Manager.Instance.CanDeleteSession(session);
                    this.addNote.Visible = Manager.Instance.CanCreateNote(session);
                }
                else if (e.Node.Tag is INote)
                {
                    INote note = e.Node.Tag as INote;

                    this.edit.Visible = Manager.Instance.CanEditNote(note);
                    this.remove.Visible = Manager.Instance.CanDeleteNote(note);
                }
            }
            else if (e.Button == System.Windows.Forms.MouseButtons.Middle)
            {
                this.expandAll_Click(sender, null);
            }
        }

        private void treeView1_MouseDown(object sender, MouseEventArgs e)
        {
            if (!this.NodeMouseDown)
            {
                if (e.Button == MouseButtons.Right)
                {
                    this.edit.Visible = false;
                    this.remove.Visible = false;
                    this.addNote.Visible = false;
                    this.addSession.Visible = false;
                    this.addProject.Visible = Manager.Instance.CanCreateProject();
                }
            }

            this.NodeMouseDown = false;
        }

        private void remove_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode == null) { return; }

            TreeNode node = this.treeView1.SelectedNode;
            object data = node.Tag;
            TreeNode parentNode = node.Parent;

            node.Remove();

            if (data is IProject)
            {
                Manager.Instance.RemoveProject((IProject)data);
            }
            else if (data is ISession)
            {
                Manager.Instance.RemoveSession((ISession)data);
                if (parentNode.Tag is DateTime)
                {
                    if (parentNode.Nodes.Count == 0)
                    {
                        parentNode = parentNode.Parent;
                        parentNode.Nodes.Clear();
                    }
                }
            }
            else if (data is INote)
            {
                Manager.Instance.RemoveNote((INote)data);
            }

            Manager.Instance.Save();

            this.SkinNodeAndParents(parentNode);
        }

        private void edit_Click(object sender, EventArgs e)
        {
            TreeNode node = this.treeView1.SelectedNode;

            if (node == null) { return; }

            this.showEditDialog(node);
        }

        private void showEditDialog(TreeNode node)
        {
            if (node.Tag is IProject)
            {
                EditForms.EditProject editProjectForm = new TimeTracker.EditForms.EditProject((IProject)node.Tag);
                editProjectForm.Saved += delegate(object sender2, Utility.Events.EventArgs<IProject> e2)
                {
                    this.Sort();
                    this.SkinNodeAndParents(node);
                };
                editProjectForm.ShowDialog();
            }
            else if (node.Tag is ISession)
            {
                EditForms.EditSession editSessionForm = new TimeTracker.EditForms.EditSession((ISession)node.Tag);
                editSessionForm.Saved += delegate(object sender2, Utility.Events.EventArgs<ISession> e2)
                {
                    this.Sort();
                    this.SkinNodeAndParents(node);
                };
                editSessionForm.ShowDialog();
            }
            else if (node.Tag is INote)
            {
                EditForms.EditNote editNoteForm = new TimeTracker.EditForms.EditNote((INote)node.Tag);
                editNoteForm.Saved += delegate(object sender2, Utility.Events.EventArgs<INote> e2)
                {
                    this.Sort();
                    this.SkinNodeAndParents(node);
                };
                editNoteForm.ShowDialog();
            }
        }

        private void addNote_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode == null) { return; }

            TreeNode node = this.treeView1.SelectedNode;

            if (node.Tag is ISession)
            {
                EditForms.AddNote addNoteForm = new TimeTracker.EditForms.AddNote((ISession)node.Tag);
                addNoteForm.Saved += delegate(object sender2, Utility.Events.EventArgs<INote> e2)
                {
                    TreeNode newNode = new TreeNode()
                    {
                        Tag = e2.Value
                    };
                    node.Nodes.Add(newNode);

                    this.Sort();
                    this.SkinNodeAndParents(newNode);
                };
                addNoteForm.ShowDialog();
            }

            node.Expand();
        }

        private void addSession_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode == null) { return; }

            TreeNode node = this.treeView1.SelectedNode;

            if (node.Tag is IProject)
            {
                EditForms.AddSession addSessionForm = new TimeTracker.EditForms.AddSession((IProject)node.Tag);
                addSessionForm.Saved += delegate(object sender2, Utility.Events.EventArgs<ISession> e2)
                {
                    TreeNode newNode = new TreeNode()
                    {
                        Tag = e2.Value
                    };

                    if (this.IsGroupByProject)
                    {
                        DateTime day = e2.Value.StartTime.ToDay();
                        TreeNode dayNode;
                        if (node.Nodes.ContainsDataBoundItem(day))
                        {
                            dayNode = node.Nodes.FindByDataBoundItem(day);
                        }
                        else
                        {
                            dayNode = new TreeNode();
                            dayNode.Tag = day;
                            node.Nodes.Add(dayNode);
                        }
                        dayNode.Nodes.Add(newNode);
                        dayNode.Expand();
                    }
                    else if (!this.IsGroupByProject)
                    {
                        node.Nodes.Add(newNode);
                    }

                    this.Sort();
                    this.SkinNodeAndParents(newNode);
                };
                addSessionForm.ShowDialog();
            }

            node.Expand();
        }

        private void addProject_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode == null) { return; }

            TreeNode node = this.treeView1.SelectedNode;

            IProject project = node.Tag is IProject ? node.Tag as IProject : null;

            EditForms.AddProject addProjectForm = new TimeTracker.EditForms.AddProject(project);
            addProjectForm.Saved += delegate(object sender2, Utility.Events.EventArgs<IProject> e2)
            {
                TreeNode newNode = new TreeNode()
                {
                    Tag = e2.Value
                };
                node.Nodes.Add(newNode);

                this.Sort();
                this.SkinNodeAndParents(newNode);
            };
            addProjectForm.ShowDialog();

            node.Expand();
        }

        // want to bind all nodes first, then skin
        protected void BindNodes(TreeNode parentNode, bool bindChildren, DoWorkEventArgs e)
        {
            if (treeWorker.CancellationPending)
            {
                e.Cancel = true;

                return;
            }

            if (parentNode.Tag == null) { return; }

            this.treeView1.SafeInvoke(delegate
            {
                parentNode.Nodes.Clear();
            });

            if (parentNode.Tag is IEnumerable<IProject>)
            {
                IEnumerable<IProject> projects = new IProject[] { };

                projects = (IEnumerable<IProject>)parentNode.Tag;
                projects = projects.OrderBy(r => r.Name);

                if (projects != null)
                {
                    foreach (IProject project in projects.ToArray())
                    {
                        TreeNode projectNode = new TreeNode();

                        this.treeView1.SafeInvoke(delegate
                        {
                            parentNode.Nodes.Add(projectNode);

                            projectNode.Tag = project;

                            if (bindChildren)
                            {
                                this.BindNodes(projectNode, bindChildren, e);
                            }
                        });
                    }
                }
            }
            else if (parentNode.Tag is IProject)
            {
                IProject project = null;

                this.treeView1.SafeInvoke(delegate
                {
                    project = (IProject)parentNode.Tag;
                });

                //Child projects
                IEnumerable<IProject> children = project.Projects;
                if (!this.IsGroupByProject)
                {
                    DateTime day = parentNode.GetClosestDateTimeFromParent();
                    children = children.Where(r => r.Duration(day) > TimeSpan.FromSeconds(0));
                }
                foreach (IProject childProject in children)
                {
                    TreeNode childProjectNode = new TreeNode();
                    childProjectNode.Tag = childProject;

                    parentNode.Nodes.Add(childProjectNode);

                    if (bindChildren)
                    {
                        this.BindNodes(childProjectNode, bindChildren, e);
                    }
                }

                if (this.IsGroupByProject)
                {
                    IEnumerable<DateTime> days = project.Sessions.ThatAreNotArchived().Days();
                    if (this.IsOrderAscending)
                    {
                        days = days.OrderBy(r => r.Ticks);
                    }
                    else
                    {
                        days = days.OrderByDescending(r => r.Ticks);
                    }


                    foreach (DateTime day in days)
                    {
                        TreeNode dayNode = new TreeNode();

                        this.treeView1.SafeInvoke(delegate
                        {
                            dayNode.Tag = day;

                            parentNode.Nodes.Add(dayNode);

                            if (bindChildren)
                            {
                                this.BindNodes(dayNode, bindChildren, e);
                            }
                        });
                    }
                }
                else if (!this.IsGroupByProject)
                {
                    DateTime day = parentNode.GetClosestDateTimeFromParent();

                    IEnumerable<ISession> sessions = project.Sessions.ThatAreNotArchived().ByDay(day);

                    if (this.IsOrderAscending)
                    {
                        sessions = sessions.OrderBy(r => r.StartTime);
                    }
                    else
                    {
                        sessions = sessions.OrderByDescending(r => r.StartTime);
                    }

                    foreach (ISession session in sessions)
                    {
                        TreeNode sessionNode = new TreeNode();

                        this.treeView1.SafeInvoke(delegate
                        {
                            sessionNode.Tag = session;

                            parentNode.Nodes.Add(sessionNode);

                            if (bindChildren)
                            {
                                this.BindNodes(sessionNode, bindChildren, e);
                            }
                        });
                    }
                }
            }
            else if (parentNode.Tag is ISession)
            {
                ISession session = null;

                this.treeView1.SafeInvoke(delegate
                {
                    session = (ISession)parentNode.Tag;
                });

                IEnumerable<INote> notes = session.Notes;
                if (this.IsOrderAscending)
                {
                    notes = notes.OrderBy(r => r.Time);
                }
                else
                {
                    notes = notes.OrderByDescending(r => r.Time);
                }

                foreach (INote note in notes)
                {
                    TreeNode noteNode = new TreeNode();

                    this.treeView1.SafeInvoke(delegate
                    {
                        noteNode.Tag = note;

                        parentNode.Nodes.Add(noteNode);
                    });
                }
            }
            else if (parentNode.Tag is INote)
            {
                INote note = (INote)parentNode.Tag;
            }
            else if (parentNode.Tag is DateTime)
            {
                DateTime day = DateTime.MinValue;

                this.treeView1.SafeInvoke(delegate
                {
                    day = (DateTime)parentNode.Tag;
                });

                if (parentNode.Parent.Tag is IProject)
                {
                    IProject project = null;

                    this.treeView1.SafeInvoke(delegate
                    {
                        project = (IProject)parentNode.Parent.Tag;
                    });

                    IEnumerable<ISession> sessions = project.Sessions.ThatAreNotArchived().ByDay(day);

                    if (this.IsOrderAscending)
                    {
                        sessions = sessions.OrderBy(r => r.StartTime);
                    }
                    else
                    {
                        sessions = sessions.OrderByDescending(r => r.StartTime);
                    }

                    foreach (ISession session in sessions)
                    {
                        TreeNode sessionNode = new TreeNode();

                        this.treeView1.SafeInvoke(delegate
                        {
                            sessionNode.Tag = session;

                            parentNode.Nodes.Add(sessionNode);

                            if (bindChildren)
                            {
                                this.BindNodes(sessionNode, bindChildren, e);
                            }
                        });
                    }
                }
                else
                {
                    IEnumerable<IProject> projects = Manager.Instance.GetProjectsForDay(day);

                    projects = projects.OrderBy(r => r.Name);

                    foreach (IProject project in projects)
                    {
                        TreeNode projectNode = new TreeNode();

                        this.treeView1.SafeInvoke(delegate
                        {
                            projectNode.Tag = project;

                            parentNode.Nodes.Add(projectNode);

                            if (bindChildren)
                            {
                                this.BindNodes(projectNode, bindChildren, e);
                            }
                        });
                    }
                }
            }
            else if (parentNode.Tag is IEnumerable<DateTime>)
            {
                IEnumerable<DateTime> days = new DateTime[] { };

                this.treeView1.SafeInvoke(delegate
                {
                    days = (IEnumerable<DateTime>)parentNode.Tag;
                });

                if (this.IsOrderAscending)
                {
                    days = days.OrderBy(r => r.Ticks);
                }
                else
                {
                    days = days.OrderByDescending(r => r.Ticks);
                }

                foreach (DateTime day in days)
                {
                    TreeNode dayNode = new TreeNode();

                    this.treeView1.SafeInvoke(delegate
                    {
                        dayNode.Tag = day;

                        parentNode.Nodes.Add(dayNode);

                        if (bindChildren)
                        {
                            this.BindNodes(dayNode, bindChildren, e);
                        }
                    });
                }
            }
        }


        private void expandAll_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode == null) { return; }

            TreeNode node = this.treeView1.SelectedNode;

            node.ExpandAll();
        }

        private void archiveSessionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.treeView1.SelectedNode == null) { return; }

            TreeNode node = this.treeView1.SelectedNode;

            this.ArchiveSessions(node);

            Manager.Instance.Save();

            this.SkinNodeAndParents(node);
        }

        private void ArchiveSessions(TreeNode parentNode)
        {
            if (parentNode.Tag is ISession)
            {
                ISession session = (ISession)parentNode.Tag;

                session.Archived = true;
            }

            if (parentNode.Nodes.Count > 0)
            {
                foreach (TreeNode node in parentNode.Nodes)
                {
                    this.ArchiveSessions(node);
                }
            }

            this.SkinNode(parentNode);
        }

        protected bool IsOrderAscending
        {
            get
            {
                bool ascending = true;

                this.comboBox1.SafeInvoke(delegate
                {
                    ascending = (string)this.comboBox1.SelectedItem == "Ascending";
                });

                return ascending;
            }
        }

        protected bool IsGroupByProject
        {
            get
            {
                bool project = true;

                this.comboBox2.SafeInvoke(delegate
                {
                    project = (string)this.comboBox2.SelectedItem == "Project";
                });

                return project;
            }
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.Sort();
        }

        private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.Bind();
        }

    }
}
